﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using IronPython.Hosting;
using Microsoft.Scripting.Hosting.Shell;
using Microsoft.Scripting.Runtime;

namespace Misuzilla.Crigate.Scripting
{
    [Description("IronPython コンソールコンテキストに切り替えます")]
    public class IpyContext : Context
    {
        private Thread _consoleThread;
        private PythonCommandLine _pythonCommandLine;
        private VirtualConsole _virtualConsole;

        public override void Initialize()
        {
            DLRIntegrationHandler handler = CurrentSession.HandlerLoader.GetHandler<DLRIntegrationHandler>();
            _virtualConsole = new VirtualConsole(CurrentSession, this.Console);
            handler.ScriptRuntime.IO.SetOutput(MemoryStream.Null, _virtualConsole.Output);
            handler.ScriptRuntime.IO.SetErrorOutput(MemoryStream.Null, _virtualConsole.Output);
            _pythonCommandLine = new PythonCommandLine2(CurrentSession);
            PythonConsoleOptions consoleOptions = new PythonConsoleOptions();

            _consoleThread = new Thread(t =>
            {
                _pythonCommandLine.Run(handler.ScriptRuntime.GetEngine("py"), _virtualConsole, consoleOptions);
            });
            _consoleThread.SetApartmentState(ApartmentState.STA);
            _consoleThread.Start();

            base.Initialize();
        }

        public override bool OnPreProcessInput(string inputLine)
        {
            if (inputLine.Trim().ToLower() == "exit")
                return false;

            _virtualConsole.SetLine((inputLine == " ") ? "" : inputLine);
            return true;
        }

        [Browsable(false)]
        public override void Help(string commandName)
        {
        }

        [Description("IronPython コンソールを終了します")]
        public new void Exit()
        {
            base.Exit();
        }

        public override void Dispose()
        {
            if (_consoleThread != null)
            {
                _consoleThread.Abort();
                _consoleThread = null;
            }
            base.Dispose();
        }

        private class VirtualWriter : TextWriter
        {
            private Console _console;
            public override Encoding Encoding { get { return Encoding.UTF8; } }
            public override void Write(string value)
            {
                if (!String.IsNullOrEmpty(value.Trim()))
                    _console.NotifyMessage(value);
            }

            public VirtualWriter(Console console)
            {
                _console = console;
            }
        }

        /// <summary>
        /// PythonCommandLine.cs の Initialize で Console.Out/Error に OutputWriter を設定しており、
        /// それはもともとの System.Console.Out などをみている
        /// </summary>
        class PythonCommandLine2 : PythonCommandLine
        {
            private Session _session;
            public PythonCommandLine2(Session session)
                : base()
            {
                _session = session;
            }
            protected override Scope CreateScope()
            {
                Scope scope = base.CreateScope();
                scope.SetObjectName("Session", _session);
                scope.SetObjectName("CurrentSession", _session);

                return scope;
            }
            protected override int Run()
            {
                Language.DomainManager.SharedIO.SetOutput(MemoryStream.Null, Console.Output);
                Language.DomainManager.SharedIO.SetErrorOutput(MemoryStream.Null, Console.Output);
                return base.Run();
            }
        }

        private class VirtualConsole : IConsole
        {
            private Session _session;
            private Console _console;
            private Queue<String> _lines;
            private ManualResetEvent _resetEvent = new ManualResetEvent(false);
            private VirtualWriter _writer;

            public VirtualConsole(Session session, Console console)
            {
                _writer = new VirtualWriter(console);
                _session = session;
                _console = console;
                _lines = new Queue<string>();
            }

            public void SetLine(String line)
            {
                _lines.Enqueue(line);
                _resetEvent.Set();
            }

            #region IConsole メンバ

            public TextWriter ErrorOutput
            {
                get { return _writer; }
                set { }
            }

            public TextWriter Output
            {
                get { return _writer; }
                set { }
            }

            public string ReadLine(int autoIndentSize)
            {
                _resetEvent.WaitOne();
                String line = _lines.Dequeue();
                if (_lines.Count == 0)
                    _resetEvent.Reset();
                return line;
            }

            public void Write(string text, Style style)
            {
                // TODO: style
                _console.NotifyMessage(text);
            }

            public void WriteLine()
            {
                _console.NotifyMessage(" ");
            }

            public void WriteLine(string text, Style style)
            {
                // TODO: style
                _console.NotifyMessage(text);
            }

            #endregion
        }
    }
}
